﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Sublimation
{
    public class SpawnPoint : GameObject
    {
        protected float rotationToCenter;
        public float RotationToCenter
        {
            get { return this.rotationToCenter; }
        }

        public SpawnPoint(WorldProperties worldProperties)
            : base(worldProperties)
        {
            LoadContent();

            // make sure it loads and draws
            DrawOrder = 10;
            UpdateOrder = 50;
            Visible = true;
            Enabled = true;

            //TODO: make this real input
            CollisionRadius = MAX_ENEMY_SPAWN_DISTANCE;
            SetInitialPosition();
            Rotation = (float)worldProperties.Random.NextDouble() * MathHelper.TwoPi;
            backgroundRotation = Rotation;
            Scale = 1f;
            SetOriginToCentre();
            SpriteColor = new Color(Color.White, 120);
            
            // set the rotation to face them in towards the map
            Vector2 vectorToCenter = new Vector2(worldProperties.Map.WorldBounds.Center.X, worldProperties.Map.WorldBounds.Center.Y) - Position;
            rotationToCenter = (float)Math.Atan2(vectorToCenter.Y, vectorToCenter.X);
        }

        public const float ROTATION_RADIANS_PER_SECOND = MathHelper.PiOver4;
        public override void DoMovement(GameTime gameTime)
        {
            base.DoMovement(gameTime);

            this.Rotation += ROTATION_RADIANS_PER_SECOND * (float)gameTime.ElapsedGameTime.TotalSeconds;
        }

        // the size of a shield frame
        private const float SPRITE_SCALE_TO_ONE = 100f;
        Texture2D backgroundSprite;
        float backgroundRotation;
        protected override void LoadContent()
        {
            base.LoadContent();

            backgroundSprite = Game.Content.Load<Texture2D>("Sprites\\nest");
            Sprite = Game.Content.Load<Texture2D>("Sprites\\nest_center");
        }

        public const int MAX_ENEMY_SPAWN_DISTANCE = 100; // enemies will spawn within the circle generated by this radius around the spawn point
        public const int MAX_DISTANCE_FROM_EDGE_OF_MAP = 200;
        public const float CLOSEST_SPAWN_TO_TOWER_AS_FACTOR_OF_RADIUS = 8.0f;
        public const float CLOSEST_SPAWN_TO_SPAWN_AS_FACTOR_OF_RADIUS = 3.0f;
        protected void SetInitialPosition()
        {
            Vector2 vectorToCenter;
            Vector2 PerimeterVector;
            do
            {
                // get a position on the edge of the map
                int perimeterPosition = worldProperties.Random.Next(0, worldProperties.Map.Perimeter);
                if (perimeterPosition <= worldProperties.Map.WorldBounds.Width)
                {
                    // place on the top
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + perimeterPosition, worldProperties.Map.WorldBounds.Y + 1);
                }
                else if (perimeterPosition <= worldProperties.Map.WorldBounds.Width + worldProperties.Map.WorldBounds.Height)
                {
                    // right side
                    perimeterPosition -= worldProperties.Map.WorldBounds.Width;
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + worldProperties.Map.WorldBounds.Width - 1, worldProperties.Map.WorldBounds.Y + perimeterPosition);
                }
                else if (perimeterPosition <= worldProperties.Map.WorldBounds.Width * 2 + worldProperties.Map.WorldBounds.Height)
                {
                    // bottom side
                    perimeterPosition -= worldProperties.Map.WorldBounds.Width + worldProperties.Map.WorldBounds.Height;
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + perimeterPosition, worldProperties.Map.WorldBounds.Y + worldProperties.Map.WorldBounds.Height - 1);
                }
                else
                {
                    // left side
                    perimeterPosition -= worldProperties.Map.WorldBounds.Width * 2 + worldProperties.Map.WorldBounds.Height;
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + 1, worldProperties.Map.WorldBounds.Y + perimeterPosition);
                }

                // now get a vecotr from the perimeter to the center
                vectorToCenter = new Vector2(worldProperties.Map.WorldBounds.Center.X, worldProperties.Map.WorldBounds.Center.Y) - PerimeterVector;

                // scale it to the maximum distance the spawn point can be from the edge
                vectorToCenter = Vector2.Normalize(vectorToCenter) * MAX_DISTANCE_FROM_EDGE_OF_MAP;

                // shrink the vector to remove collisions with bad spawn locations
                foreach (Player player in worldProperties.Players)
                {
                    foreach (Tower tower in player.Towers)
                    {
                        // do we collide with this tower?
                        if (Vector2.Distance(PerimeterVector + vectorToCenter, tower.Position) <= Tower.RADIUS * CLOSEST_SPAWN_TO_TOWER_AS_FACTOR_OF_RADIUS)
                        {
                            // remove the amount of collision from the vector length
                            float distanceToEdgeOfCollision = Vector2.Distance(PerimeterVector, tower.Position) - Tower.RADIUS * CLOSEST_SPAWN_TO_TOWER_AS_FACTOR_OF_RADIUS;
                            if (distanceToEdgeOfCollision <= 0) distanceToEdgeOfCollision = vectorToCenter.Length();
                            vectorToCenter = Vector2.Normalize(vectorToCenter) * (vectorToCenter.Length() - distanceToEdgeOfCollision);
                        }
                    }
                }

                for (int i = 0; i < WorldProperties.MAX_SPAWN_POINTS; i++)
                {
                    if (worldProperties.spawnPoints[i] != null && worldProperties.spawnPoints[i] != this)
                    {
                        SpawnPoint spawn = worldProperties.spawnPoints[i];
                        // do we collide with this spawn point?
                        if (Vector2.Distance(PerimeterVector + vectorToCenter, spawn.Position) <= spawn.CollisionRadius * CLOSEST_SPAWN_TO_SPAWN_AS_FACTOR_OF_RADIUS)
                        {
                            // remove the amount of collision from the vector length
                            float distanceToEdgeOfCollision = Vector2.Distance(PerimeterVector, spawn.Position) - spawn.CollisionRadius * CLOSEST_SPAWN_TO_SPAWN_AS_FACTOR_OF_RADIUS;
                            if (distanceToEdgeOfCollision <= 0) distanceToEdgeOfCollision = vectorToCenter.Length();
                            vectorToCenter = Vector2.Normalize(vectorToCenter) * (vectorToCenter.Length() - distanceToEdgeOfCollision);
                        }
                    }
                }
            } while (vectorToCenter.Length() <= CollisionRadius);

            // now we should have a vector from the edge to a point in the direction of the center
            // anywhere on this is a valid spawn point
            //XXX: havent taken collision radius of spawn point into account when working out the vector

            // get a random position on the vector
            float spawnPosition = (float)worldProperties.Random.NextDouble() * (vectorToCenter.Length() - CollisionRadius) + CollisionRadius;
            // scale the vector to this position
            vectorToCenter = Vector2.Normalize(vectorToCenter) * spawnPosition;
            // get this as a position from the perimeter and set Position
            Position = PerimeterVector + vectorToCenter;
        }

        public override void Draw(GameTime gameTime)
        {
            // draw the background
            spriteBatch.Begin();
            spriteBatch.Draw(backgroundSprite, Position, null, SpriteColor, backgroundRotation, Origin, Scale, SpriteEffects.None, 1);
            spriteBatch.End();

            // draw the spinning circle
            base.Draw(gameTime);
        }
    }
}